<template>
  <view class="mxp-input" :style="[{ background: background || defThemeColor.fill }, cusStyle]" :class="[cusClass]">
    <view class="left-icon-box">
      <slot name="prefix">
        <mxp-icon-image v-if="prefixIcon" :icon="prefixIcon" :size="iconSize"></mxp-icon-image>
      </slot>
    </view>
    <input
      v-model="data.innerValue"
      :style="[inputStyle, { textAlign }]"
      class="input"
      :maxlength="maxlength"
      :type="data.newType"
      :focus="focus"
      :password="data.newPassword"
      :confirmType="confirmType"
      :placeholder="newPlaceholder"
      :placeholderStyle="newPlaceholderStyle"
      :placeholderClass="placeholderClass"
      :cursorSpacing="cursorSpacing"
      :adjustPosition="adjustPosition"
      :selectionStart="selectionStart"
      :selectionEnd="selectionEnd"
      :confirmHold="confirmHold"
      :controlled="controlled"
      :alwaysSystem="alwaysSystem"
      :disabled="disabled"
      :autoBlur="autoBlur"
      :alwaysEmbed="alwaysEmbed"
      :holdKeyboard="holdKeyboard"
      :ignoreCompositionEvent="ignoreCompositionEvent"
      @blur="onBlurEvent"
      @focus="onFocusEvent"
      @input="onInputEvent"
      @confirm="onConfirm"
      @keyboardheightchange="onKeyboardheightchange"
    />
    <view class="right-icon-box">
      <view v-if="data.innerValue !== '' && !disabled && clearable" class="right-butt" @click.stop.prevent="cancelValueClick">
        <mxp-icon name="guanbi1" :color="iconColor" :size="iconSize" :cusStyle="iconStyle"></mxp-icon>
      </view>
      <view v-if="password" key="view-2" class="right-butt" @click="switchPassword">
        <mxp-icon :name="data.newPassword ? 'xianshi' : 'yincang'" :color="iconColor" :size="iconSize" :cusStyle="iconStyle"></mxp-icon>
      </view>
      <slot name="suffix">
        <mxp-icon-image v-if="suffixIcon" :icon="suffixIcon" :size="iconSize"></mxp-icon-image>
      </slot>
    </view>
  </view>
</template>

<script lang="ts" setup>
  import { type CSSProperties, type PropType, computed, inject, nextTick, onMounted, shallowReactive, watch } from 'vue';
  import { type FormID, formInjectKey, formItemPropInjectKey, mxpFormStore } from '../mxp-tool/store/mxp-store-form';
  import { publicStyeProps } from '../mxp-tool/utils/public-props';
  import MxpIcon from '@/mxp-ui/mxp-icon/mxp-icon.vue';
  import MxpIconImage from '@/mxp-ui/mxp-icon-image/mxp-icon-image.vue';
  import type { $AllColorStr, $SizeUnitStr } from '@/mxp-ui/mxp-tool/types/interface';
  import { cssObj2Str } from '../mxp-tool/utils/dom';
  import type {
    InputConfirmType,
    InputOnBlurEvent,
    InputOnConfirmEvent,
    InputOnFocusEvent,
    InputOnInputEvent,
    InputOnKeyboardheightchangeEvent,
    InputType,
  } from 'mxp-uni-types';
  import { defThemeColor } from '../mxp-tool/theme/theme';
  defineOptions({
    name: 'mxp-input',
  });

  const props = defineProps({
    /** 输入框的样式 */
    inputStyle: { type: Object as PropType<CSSProperties>, default: () => ({}) },
    /** 对齐方式 */
    textAlign: { type: String as PropType<'center' | 'left' | 'right'>, default: 'left' },
    /** 是否自动聚焦 */
    focus: { type: Boolean, default: false },
    /** 前缀图片的 Url */
    prefixIcon: { type: String, default: '' },
    /** 后缀图片的 Url */
    suffixIcon: { type: String, default: '' },
    /** 图标颜色 */
    iconStyle: { type: Object as PropType<CSSProperties>, default: () => ({}) },
    /** 是否为密码文本 */
    password: { type: Boolean, default: false },
    /** 是否显示清除按钮图标 */
    clearable: { type: Boolean, default: true },
    /** 是否显示清除按钮图标 */
    background: { type: String as PropType<$AllColorStr>, default: undefined },
    /** 绑定的值 */
    modelValue: { type: [String, Number], default: '' },
    /** 指定placeholder的样式类，注意页面或组件的style中写了scoped时，需要在类名前写/deep/部分平台可能还需要写到 app.vue中才生效 */
    placeholderClass: { type: String, default: 'textarea-placeholder' },
    /** 指定placeholder的样式 */
    placeholderStyle: { type: Object as PropType<CSSProperties>, default: () => ({ color: defThemeColor.value.text3 }) },
    /** 最大长度 */
    maxlength: { type: Number, default: 140 },
    /** 设置右下角按钮的文字，兼容性详见uni-app文档 */
    confirmType: { type: String as PropType<InputConfirmType>, default: 'go' },
    /** 输入框类型 */
    type: { type: String as PropType<InputType>, default: 'text' },
    /** 输入框为空时的占位符 */
    placeholder: { type: [String, Number], default: '请输入...' },
    /** 指定光标与键盘的距离，取 input 距离底部的距离和 cursor-spacing 指定的距离的最小值作为光标与键盘的距离 */
    cursorSpacing: { type: Number, default: 8 },
    /** 键盘弹起时，是否自动上推页面, App-Android(vue 页面 softinputMode 为 adjustResize 时无效，使用 x5 内核时无效)、微信小程序、百度小程序、QQ小程序、京东小程序 */
    adjustPosition: { type: Boolean, default: true },
    /** 是否禁用 */
    disabled: { type: Boolean, default: false },
    /** 点击键盘右下角按钮时是否保持键盘不收起 */
    confirmHold: { type: Boolean, default: false },
    /** 光标起始位置，自动聚集时有效，需与selection-end搭配使用 */
    selectionStart: { type: Number, default: undefined },
    /** 光标结束位置，自动聚集时有效，需与selection-start搭配使用 */
    selectionEnd: { type: Number, default: undefined },
    /** 是否为受控组件。为 true 时，value 内容会完全受 setData 控制 (支付宝小程序 1.9.0+) */
    controlled: { type: Boolean, default: false },
    /** 键盘收起时，是否自动失去焦点 (App-Vue 3.0.0+) */
    autoBlur: { type: Boolean, default: false },
    /** 是否忽略组件内对文本合成系统事件的处理。为 false 时将触发 compositionstart、compositionend、compositionupdate 事件，且在文本合成期间会触发 input 事件 (App-vue (3.4.4+)、H5 (3.4.4+)、App-nvue不支持) */
    ignoreCompositionEvent: { type: Boolean, default: true },
    /** 是否忽略组件内对文本合成系统事件的处理。为 false 时将触发 compositionstart、compositionend、compositionupdate 事件，且在文本合成期间会触发 input 事件 (App-vue (3.4.4+)、H5 (3.4.4+)、App-nvue不支持) */
    holdKeyboard: { type: Boolean, default: false },
    /** 是否强制使用系统键盘和 Web-view 创建的 input 元素。为 true 时，confirm-type、confirm-hold 可能失效  (支付宝小程序 2.7.3+) */
    alwaysSystem: { type: Boolean, default: false },
    /** 强制 input 处于同层状态，默认 focus 时 input 会切到非同层状态 (微信小程序 2.10.4+) */
    alwaysEmbed: { type: Boolean, default: false },
    ...publicStyeProps,
  });

  defineSlots<{
    /** 输入框头部内容 ，只对非 type="textarea" 有效 */
    prefix: () => void;
    /** 输入框尾部内容，只对非 type="textarea" 有效 */
    suffix?: () => void;
  }>();

  const emits = defineEmits<{
    (e: 'update:modelValue', str: number | string): void;
    (e: 'input', event: InputOnInputEvent): void;
    (e: 'blur', event: InputOnBlurEvent): void;
    (e: 'focus', event: InputOnFocusEvent): void;
    (e: 'keyboardheightchange', event: InputOnKeyboardheightchangeEvent): void;
    (e: 'confirm', event: InputOnConfirmEvent): void;
    (e: 'change', str: number | string): void;
    (e: 'cancel', str: number | string): void;
  }>();

  const data = shallowReactive({
    innerValue: props.modelValue || '',
    newType: props.type,
    newPassword: props.password,
    okValue: '' as number | string, // 保存上次的正确结果
  });

  watch(
    () => props.modelValue,
    (newVal) => {
      data.innerValue = newVal;
    }
  );
  watch(
    () => props.password,
    (newVal) => {
      data.newPassword = newVal;
    }
  );

  const iconSize = computed(() => (props.iconStyle.fontSize || props.iconStyle['font-size'] || '16px') as $SizeUnitStr);
  const iconColor = computed(() => (props.iconStyle.color || defThemeColor.value.text3) as $AllColorStr);

  /** 最终的占位字符串 */
  const newPlaceholder = computed(() => String(props.placeholder || ''));

  /** 最终的占位文字样式 */
  const newPlaceholderStyle = computed(() => cssObj2Str(props.placeholderStyle));

  let mxpFromIDInjectKey: FormID | undefined = undefined;
  let propInjectKey: number | string | undefined = undefined;
  onMounted(() => {
    mxpFromIDInjectKey = inject<FormID>(formInjectKey);
    propInjectKey = inject<number | string>(formItemPropInjectKey);
  });

  /**
   * 清除输入
   */
  function cancelValueClick() {
    nextTick(() => {
      data.innerValue = '';
      emits('update:modelValue', data.innerValue);
      setTimeout(() => {
        emits('cancel', data.innerValue);
        emits('change', data.innerValue);
        // 尝试调用mxp-form组件的验证
        const validateField = mxpFromIDInjectKey ? mxpFormStore().state[mxpFromIDInjectKey]?.validateField : undefined;
        typeof validateField === 'function' && (propInjectKey || propInjectKey === 0) && validateField(propInjectKey, 'change');
      }, 10);
    });
  }

  /** 点击键盘完成按钮触发 */
  function onConfirm(event: InputOnConfirmEvent) {
    try {
      uni.hideKeyboard();
    } catch (e) {}
    nextTick(() => {
      emits('confirm', event);
    });
  }

  /** 显示或隐藏密码 */
  function switchPassword() {
    data.newPassword = !data.newPassword;
  }

  //
  /** 改变时触发 */
  function onInputEvent(event: InputOnInputEvent) {
    setTimeout(() => {
      data.innerValue = event.detail.value;
      emits('update:modelValue', data.innerValue);
      emits('change', data.innerValue);
      emits('input', event);
      // 尝试调用mxp-form组件的验证
      const validateField = mxpFromIDInjectKey ? mxpFormStore().state[mxpFromIDInjectKey]?.validateField : undefined;
      typeof validateField === 'function' && (propInjectKey || propInjectKey === 0) && validateField(propInjectKey, 'change');
    }, 10);
  }

  /** 获得焦点 */
  function onFocusEvent(event: InputOnFocusEvent) {
    emits('focus', event);
  }

  /** 失去焦点 */
  function onBlurEvent(event: InputOnBlurEvent) {
    emits('blur', event);
    // 尝试调用mxp-form组件的验证
    const validateField = mxpFromIDInjectKey ? mxpFormStore().state[mxpFromIDInjectKey]?.validateField : undefined;
    typeof validateField === 'function' && (propInjectKey || propInjectKey === 0) && validateField(propInjectKey, 'blur');
  }

  /** 键盘高度发生变化的时候触发此事件，event.detail = {height: height, duration: duration} */
  function onKeyboardheightchange(event: InputOnKeyboardheightchangeEvent) {
    emits('keyboardheightchange', event);
  }
</script>

<style lang="scss" scoped>
  .mxp-input {
    display: flex;
    flex-flow: row nowrap;
    align-items: center;
    justify-content: flex-start;

    .left-icon-box {
      display: flex;
      flex-flow: row nowrap;
      align-items: center;
      justify-content: center;
    }

    .input {
      flex: 1;
      height: 38px;
      padding: 0 10px;
      font-size: 14px;
      line-height: 14px;
      text-align: left;
      text-shadow: none;
      user-select: text;
      background-color: transparent;
      border: none;
      border-style: none;
      outline: none;
      outline-color: transparent;
      box-shadow: none;
      appearance: none;
    }

    .right-icon-box {
      display: flex;
      flex-flow: row nowrap;
      align-items: center;
      justify-content: flex-end;

      .right-butt {
        padding: 4px;

        .iconfont {
          display: flex;
          flex-flow: row nowrap;
          align-items: center;
          justify-content: center;
          width: 16px;
          height: 16px;
          font-size: 16px;
          line-height: 16px;
        }
      }
    }
  }
</style>
